home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / news / nntp / nntplink3.1.0 / remote.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-02  |  9.5 KB  |  467 lines

  1. /*
  2.  * nntp communications functions
  3.  */
  4.  
  5. #include "conf.h"
  6. #include <sys/types.h>
  7. #include <errno.h>
  8. #include <ctype.h>
  9. #include <setjmp.h>
  10. #include <signal.h>
  11. #ifdef HAVE_STRING_H
  12. #include <string.h>
  13. #else
  14. #include <strings.h>
  15. #endif
  16. #ifdef FAKESYSLOG
  17. #include "fsyslog.h"
  18. #else
  19. #include <syslog.h>
  20. #endif
  21. #include <sys/socket.h>
  22. #ifdef MMAP
  23. #include <sys/stat.h>
  24. #include <sys/mman.h>
  25. #endif
  26. #include <sys/time.h>
  27. #include "readline.h"
  28. #include "nntplink.h"
  29. #include "nntp.h"
  30. #include "strfuns.h"
  31.  
  32. extern Boolean Abort_signaled;
  33. extern Boolean Debug;
  34. extern int Input_from;
  35. extern Boolean Log_close;
  36. extern long Idle_time;
  37. extern int Dtablesize;
  38.  
  39. extern void fail();
  40. extern void log();
  41.  
  42. extern char *E_fdopen;
  43.  
  44. void close_connection();
  45.  
  46. #ifndef HAVE_SELECT
  47. SIGRET    to_read_reply();
  48. static    jmp_buf    RRstack;
  49. int ignore_sigalrm = TRUE;
  50. #endif
  51.  
  52. FileBuf    *rmt_rd_fbp;
  53. FILE    *rmt_wr_fp;
  54.  
  55. /*
  56.  * send cmd to remote, terminated with a CRLF.
  57.  */
  58. Boolean
  59.   send_command(command, log_error)
  60. char *command;
  61. Boolean log_error;
  62. {
  63.     static char *fname = "send_command: ";
  64.  
  65.     dlog(LOG_DEBUG, "", "%s>>> %s\n", command);
  66.  
  67.     Idle_time = 0L;
  68.  
  69.     (void) fprintf(rmt_wr_fp, "%s\r\n", command);
  70.     (void) fflush(rmt_wr_fp);
  71.  
  72.     if (ferror(rmt_wr_fp)) {
  73.     if (log_error)
  74.       log(LOG_WARNING, fname, "%s%s: error sending command: %s\n",
  75.           Host.name, errmsg(errno));
  76.     return FALSE;
  77.     }
  78.     return TRUE;
  79. }
  80.  
  81.  
  82. #ifndef HAVE_SELECT
  83. SIGRET
  84. to_read_reply()
  85. {
  86. #ifdef BROKEN_SIGNAL
  87.     signal(SIGALRM, SIG_IGN);
  88.     signal(SIGALRM, to_read_reply);
  89. #endif
  90.  
  91.     if (!ignore_sigalrm)
  92.     LONGJMP(RRstack, 1);
  93.  
  94. }
  95. #endif /* !HAVE_SELECT */
  96.  
  97.  
  98. /**
  99.  ** read a reply line from the remote server and return the code number
  100.  ** as an integer, and the message in a buffer supplied by the caller.
  101.  ** Returns NULL if something went wrong.
  102.  **/
  103. char *
  104.   read_reply(resp)
  105. int *resp;
  106. {
  107.     static char *fname = "read_reply: ";
  108.     char *cp;
  109.     char *reply;
  110.     unsigned int rep_len;
  111. #ifdef HAVE_SELECT
  112.     int cnt;
  113.     fd_set readfds;
  114.     static struct timeval timeout = {0, 0};
  115. #endif
  116.  
  117.     *resp = FAIL;        /* assume we're gonna fail */
  118.  
  119. #ifdef HAVE_SELECT
  120.  
  121.     timeout.tv_sec = READ_TIMEOUT;
  122.     FD_ZERO (&readfds);
  123.     FD_SET (fb_fileno(rmt_rd_fbp), &readfds);
  124.     if ((cnt = select(Dtablesize, &readfds, 0, 0, &timeout)) == 0) {
  125.       log(LOG_WARNING, fname,
  126.       "%s%s: connection timed out while reading reply\n", Host.name);
  127.       return NULL;
  128.     } else if (cnt < 0) {
  129.       log(LOG_WARNING, fname,
  130.       "%s%s: select() error while reading reply: %s",
  131.       Host.name, errmsg(errno));
  132.       return NULL;
  133.     }
  134.  
  135.     reply = fb_readline(rmt_rd_fbp, &rep_len);
  136.     if (fb_err(rmt_rd_fbp))
  137.       log(LOG_WARNING, fname, "%s%s: read() error reading reply: %s\n",
  138.       Host.name, errmsg(errno));
  139.  
  140. #else
  141.     if (SETJMP(RRstack)) {
  142.     (void) alarm(0);        /* reset alarm clock */
  143.     errno = ETIMEDOUT;        /* connection timed out */
  144.     log(LOG_WARNING, fname,
  145.         "%s%s: connection timed out while reading reply\n",
  146.         Host.name);
  147.     return NULL;            /* bad read, remote time out */
  148.     }
  149.  
  150.     ignore_sigalrm = FALSE;
  151.     (void) alarm(READ_TIMEOUT);
  152.  
  153.     reply = fb_readline(rmt_rd_fbp, &rep_len);
  154.     if (fb_err(rmt_rd_fbp))
  155.       log(LOG_WARNING, fname, "%s%s: error reading reply: %s\n",
  156.       Host.name, errmsg(errno));
  157.  
  158.     ignore_sigalrm = TRUE;
  159.     (void) alarm(0);            /* reset alarm clock */
  160.  
  161. #endif /* HAVE_SELECT */
  162.  
  163.     if (reply == NULL)
  164.       return NULL;
  165.  
  166.     /*
  167.      * Make sure that what the remote sent us had a CRLF at the end
  168.      * of the line, and then null it out.
  169.      */
  170.     if (rep_len > 2 && *(cp = &reply[rep_len - 1]) == '\r')
  171.       *cp = '\0';
  172.     else {
  173.     log(LOG_WARNING, fname, "%s%s: bad reply from remote: %s\n",
  174.         Host.name, reply);
  175.     return NULL;    /* error reading from remote */
  176.     }
  177.  
  178.     dlog(LOG_DEBUG, "", "%s%s\n", reply);
  179.  
  180.     /*
  181.      * Skip any non-digits leading the response code 
  182.      * and then convert the code from ascii to integer for
  183.      * return from this routine.
  184.      */
  185.     cp = reply;
  186.     while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
  187.       cp++;    /* skip anything leading */
  188.  
  189.     if (*cp == '\0' || !isascii(*cp)) {
  190.     log(LOG_WARNING, fname, "%s%s: No response code in reply: %s\n",
  191.         Host.name, reply);
  192.     return NULL;    /* error reading from remote */
  193.     }
  194.  
  195.     *resp = atoi(cp);
  196.  
  197.     return reply;
  198. }
  199.  
  200.  
  201. char *
  202.   converse(line, resp)
  203. char *line;
  204. int *resp;
  205. {
  206.     char *reply;
  207.  
  208.     if (!send_command(line, DO_LOG_ERROR))
  209.       return NULL;
  210.  
  211.     while(((reply = read_reply(resp)) != NULL) &&
  212.       (*resp >= 100) && (*resp < 200))
  213.       ;
  214.  
  215.     if (*resp == FAIL)
  216.       close_connection(!SEND_QUIT_MSG);
  217.  
  218.     return reply;
  219. }
  220.  
  221.  
  222. /*
  223.  * open_connection - Contact the remote server and set up the two global
  224.  *                   FILE pointers to that descriptor.
  225.  *
  226.  */
  227. Boolean
  228.   open_connection(remote_server, transport)
  229. char *remote_server;
  230. int transport;
  231. {
  232.     static char *fname = "open_connection: ";
  233.  
  234.     int        socket0, socket1;
  235.     char    *reply;
  236.     int        resp;
  237.  
  238.     switch(transport) {
  239.       case T_IP_TCP:
  240.     socket0 = get_tcp_conn(remote_server, "nntp");
  241.     break;
  242.  
  243.       case T_DKHOST:
  244. #ifdef DKHOST
  245.     socket0 = get_dk_conn(remote_server);
  246. #else
  247.     fail(fname, "%sno DKHOST support compiled in\n");
  248. #endif
  249.     break;
  250.     
  251.       case T_DECNET:
  252. #ifdef DECNET
  253.     (void) signal(SIGPIPE, SIG_IGN);
  254.     socket0 = dnet_conn(remote_server, "NNTP", SOCK_STREAM, 0, 0, 0, 0);
  255.     if (socket0 < 0) {
  256.         switch(errno) {
  257.           case EADDRNOTAVAIL:
  258.         socket0 = NOHOST;
  259.         break;
  260.           case ESRCH:
  261.         socket0 = NOSERVICE;
  262.         break;
  263.         }
  264.     }
  265. #else
  266.     fail(fname, "%sno DECNET support compiled in\n");
  267. #endif
  268.     break;
  269.     }
  270.  
  271.     if (socket0 < 0) {
  272.     switch(socket0) {
  273.       case NOHOST:
  274.         log(LOG_WARNING, fname, "%s%s: host unknown\n", remote_server);
  275.         return FALSE;
  276.       case NOSERVICE:
  277.         log(LOG_WARNING, fname, "%s%s: service unknown: nntp\n",
  278.         remote_server);
  279.         return FALSE;
  280.       case FAIL:
  281.         log(LOG_NOTICE, fname, "%s%s: socket(): %s\n", remote_server,
  282.         errmsg(errno));
  283.         return FALSE;
  284.     }
  285.     }
  286.  
  287.     if ((socket1 = dup(socket0)) < 0) {
  288.     log(LOG_WARNING, fname, "%s%s: dup(%d): %s\n", remote_server, socket0,
  289.         errmsg(errno));
  290.     CLOSE(socket0);
  291.     return FALSE;
  292.     }
  293.  
  294.     if ((rmt_wr_fp = fdopen(socket1, "w")) == NULL) {
  295.     log(LOG_WARNING, fname, E_fdopen, remote_server, socket1, "w",
  296.         errmsg(errno));
  297.     CLOSE(socket0);
  298.     CLOSE(socket1);
  299.     return FALSE;
  300.     }
  301.  
  302.     rmt_rd_fbp = fb_fdopen(socket0);
  303.  
  304.     Time.begin_real = time(NULL);
  305.  
  306.     reply = read_reply(&resp);
  307.     ++Stats.connects;
  308.  
  309.     switch(resp) {
  310.       case OK_CANPOST:
  311.       case OK_NOPOST:
  312.     break;
  313.  
  314.       default:
  315.     if (reply != NULL)
  316.       log(LOG_NOTICE, fname, "%s%s: greeted us with %s\n", remote_server,
  317.           reply);
  318.     CLOSE(socket0);
  319.     fb_close(rmt_rd_fbp);
  320.     FCLOSE(rmt_wr_fp);
  321.     return FALSE;
  322.     }
  323.  
  324.     Host.connected = TRUE;
  325.     Idle_time = 0L;
  326.     return TRUE;
  327. }
  328.  
  329.  
  330. /*
  331.  * close the connection with the remote server.
  332.  *
  333.  * We trap SIGPIPE because the socket might already be gone.
  334.  */
  335. void
  336.   close_connection(send_quit)
  337. Boolean    send_quit;
  338. {
  339.     register SIGRET (*pstate)() = signal(SIGPIPE, SIG_IGN);
  340.     int resp;
  341.  
  342.     Host.connected = FALSE;
  343.     Stats.since_close = 0L;
  344.  
  345.     if (rmt_wr_fp == NULL)
  346.       return;
  347.  
  348.     if (send_quit && !send_command("QUIT", DONT_LOG_ERROR))
  349.       /*
  350.        * I don't care what they say to me; this is just being polite.
  351.        */
  352.       (void) read_reply(&resp);
  353.  
  354.     CLOSE(fb_fileno(rmt_rd_fbp));
  355.     fb_close(rmt_rd_fbp);
  356.     FCLOSE(rmt_wr_fp);
  357.     (void) signal(SIGPIPE, pstate);
  358.  
  359.     Time.end_real = time(NULL);
  360.     Time.elapsed = Time.end_real - Time.begin_real;
  361.     Time.begin_real = 0;
  362.  
  363.     if (Log_close)
  364.       log_stats();
  365.  
  366.     return;
  367. }
  368.  
  369. /**
  370.  ** send the contents of an open file descriptor to the remote,
  371.  ** with appropriate RFC822 filtering (e.g. CRLF line termination,
  372.  ** and dot escaping). Return FALSE if something went wrong.
  373.  **/
  374. Boolean
  375.   send_connection(fbp)
  376. FileBuf *fbp;
  377. {
  378.     register char    *cp;
  379.     static char        *fname = "send_connection: ";
  380.  
  381. #ifdef MMAP
  382.     register int    c;
  383.     register int    nl = TRUE;    /* assume we start on a new line */
  384.     register char *mbufr, *mptr;
  385.     long msize;
  386.     struct stat sbuf;
  387. #endif /* MMAP */
  388.  
  389. /*
  390. ** I'm using putc() instead of fputc();
  391. ** why do a subroutine call when you don't have to?
  392. ** Besides, this ought to give the C preprocessor a work-out.
  393. */
  394. #ifdef MMAP
  395. #define    PUTC(c) if (putc(c, rmt_wr_fp) == EOF) {\
  396.     (void) munmap (mbufr, sbuf.st_size);\
  397.     return FALSE; }
  398. #endif /* MMAP */
  399.  
  400.     if (fbp == NULL)
  401.       return FALSE;
  402.  
  403. #ifdef MMAP
  404.     /* map the article into memory */
  405.     (void) fstat(fb_fileno(fbp), &sbuf);
  406.     mbufr = mmap (0, sbuf.st_size, PROT_READ, MAP_PRIVATE,
  407.           fb_fileno(Article.fbp), 0);
  408.     if(mbufr == (char *) -1)
  409.       return send_command(".", DO_LOG_ERROR);
  410.  
  411.     Idle_time = 0L;
  412.  
  413.     mptr = mbufr;         /* start of article in memory */
  414.     msize = sbuf.st_size; /* size of article (bytes) */
  415.     while(msize-- > 0) {
  416.     c = *mptr++;
  417.     switch(c) {
  418.       case '\n':
  419.         PUTC('\r');        /* \n -> \r\n */
  420.         PUTC(c);
  421.         nl = TRUE;        /* for dot escaping */
  422.         break;
  423.  
  424.       case '.':
  425.         if (nl) {
  426.         PUTC(c);    /* add a dot */
  427.         nl = FALSE;
  428.         }
  429.         PUTC(c);
  430.         break;
  431.  
  432.       default:
  433.         PUTC(c);
  434.         nl = FALSE;
  435.         break;
  436.     }
  437.     }
  438.  
  439.     if (!nl) {
  440.     PUTC('\r');
  441.     PUTC('\n');
  442.     }
  443.     (void) munmap (mbufr, sbuf.st_size);
  444.  
  445. #else /* !MMAP */
  446.  
  447.     Idle_time = 0L;
  448.  
  449.     while((cp = fb_readline(fbp, NULL)) != NULL) {
  450.     if (*cp == '.')
  451.       putc('.', rmt_wr_fp);
  452.     if ((fputs(cp, rmt_wr_fp) == EOF) || (fputs("\r\n", rmt_wr_fp) == EOF))
  453.       return FALSE;
  454.     }
  455.  
  456.     if (fb_error(fbp))
  457.       log(LOG_WARNING, fname, "%s%s: error reading %s: %s\n",
  458.       Host.name, Article.filename, errmsg(errno));
  459.  
  460.     if (fflush(rmt_wr_fp) != 0)
  461.       return FALSE;
  462.  
  463. #endif /* MMAP */
  464.  
  465.     return send_command(".", DO_LOG_ERROR);
  466. }
  467.